This notebook is used to document the steps for uploading geospatial data into Google Earth Engine (GEE). These steps could change or vary depending on the needs of the end-user. GEE already provides an extensive catalog of public datasets. This catalog keeps growing based on users’ suggestions by using an issue tracking system called Issue Tracker.

Animation showing hyperspectral data visualization in GEE from the NEON 2017 Campaign at Santa Rita Experimental Range. Access to this in GEE browser API: http://bit.ly/hyper_srer_2017

Left panel shows plots with the spectral signature profile of the 426 bands for any given pixel. Other layers shown are RGB-Camera 10cm pixel size, and from 1m hyperspectral data a False color-NIR, Natural Color, and NDVI.

The tasks described below are performed using the server STORM @USDA-ARS-SWRC. These are the specifications of the server as May/2018 along with the operating system and software used:

Server: Dell R930 (storm)
O.S.: RedHat 7.5 (3.10.0-862.6.3.el7.x86_64)
Python ver.: 3.64
Google Cloud SDK ver.: 4.28 (Staging platform)
Google Python API client library (Python functions by Google)
Google Earthengine API (Python functions to access GEE)

1. Installation of Google Cloud Software Development Kit (SDK)

Google Cloud Storage is needed to serve as a staging platform to move data into GEE.

You can get SDK from here: https://cloud.google.com/sdk/docs/quickstart-linux

# ******************** Command Line Instructions ******************************#
# Unzip gc sdk 

tar -xvf google-cloud-sdk-xxx.0.0-linux-x86_64.tar.gz    

# Install it                                                                                               
./google-cloud-sdk/install.sh          

 # Refresh environment without doing logout                                                                                                    
. ~/.bashrc          

2. Installation of google-api-python-client and earthengine-api.

This setup will enable your system to access GEE platform from Python (import ee) and also use the earthengine command line tool to move data from Google Cloud into GEE.

# ******************** Command Line Instructions ******************************#
# Installing Google API-Python-client library
sudo pip install google-api-python-client

# Ensure crypto library is available. No error is expected after running the following:
python -c "from oauth2client import crypt"

# If no error, continue with the Installation of Earth Engine Python API

sudo pip install earthengine-api

# Switch the expanded directory and run script
cd earthengine-api-VERSION
python setup.py install

# Authenticate into GEE from terminal. The following line should show a URL 
# that you can paste into a browser window to authenticate with your account
# that has access to GEE.
python -c "import ee; ee.Initialize()"

After previous steps, GEE API should be ready to use it.

3. Moving data from a local drive into GEE

The workflow to move data is:

LocalDrive –> Google Cloud Storage (bucket) –> GEE (asset)

One of the current limitations on uploading several files into GEE is that you must use Google Cloud Storage(GCS) as the staging platform. You can also upload data using the JavaScript Code Editor, however, this approach will only allow you to upload single files, one by one instead of using a batch process. In the image below:

Using the Table upload is the right option to upload vector data as shapefiles.

Datasets in GEE are called assets and to upload into assets we need to create the folder structure as needed.

All the datasets in GEE are called assets: Images, Image Collections, and Tables(shp files).

To start working with assets from the command line we need to use the command line (CLI) earthengine (available as part of the GEE API installation).

4. Authentication to Google Cloud

These steps will let you authenticate into Google Cloud. We need this as a staging platform between local drive and GEE.

# ******************** Command Line Instructions ******************************#
# This command will try either to open a browser or display a link that you can
# paste into browser window.
gcloud auth login

# Set project ID as created at https://console.cloud.google.com/storage/
gcloud config set project xxxx-yyyy-172116

# Create a bucket, a folder for data staging
gsutil mb gs://my_bucket

# list buckets you have in your project
gsutil ls gs://

5. Uploading files from local disk into Google Cloud

A single copy can be made from the command line once you are authenticated into Google Cloud.

# ******************** Command Line Instructions ******************************#
gsutil -m cp *.tif gs://my_bucket/folder_A/

This will launch one task per file and will run in parallel by using available cores.

6. Using earthengine command to create folders/image collections

Before moving data into GEE you should create a target folder/image collection either using the earthengine CLI or the GEE Javascript code editor https://code.earthengine.google.com The following bash block shows an example of how to use the CLI earthengine tool to create a folder and an image collection.

# ******************** Command Line Instructions ******************************#
# Here we create a folder in GEE using earthengine tool
earthengine create folder users/gponce/usda_ars/shapefiles/AZ_LAND
# Create a collection (it looks like another folder, but internally it will be
# an image collection).
earthengine create collection users/my_username/collection_id

7. Transfer files from GCS bucket into GEE Asset

The following script can be saved as a bash script (transferGC2GE.sh) and run it as: ./transferGC2GE.sh name_bucket_gcloud name_asset

where: - name_bucket_gcloud : name of the bucket created in Google Cloud - name_asset: relative path in GEE. E.g. if you have path like users/my_user/FolderA you should only include my_user/FolderA as the second parameter.

# ******************** Bash script ********************************************#
#!/usr/bin/env bash
# Usage:
# ./transfer_to_gee.sh src_bucket dest_asset

result=`earthengine create collection users/$2`
if `test -z "$result"`; then
    echo $result
    exit 1
fi
# In the following loop we get the entire path to all the geotifs using the specified 
# Gcloud bucket. Each file will have a format like this: gs://my_gee_bucket/FILE_January2000.tif
# Each call to earthengine it will launch a task that you can monitor in the JS Code editor 
# at the "tasks" tab.
for geotiff in `gsutil ls gs://$1/*.tif`; do  
    filename=`basename $geotiff`
    asset_id="${filename%.*}"
    earthengine upload image --asset_id=users/$2/$asset_id $geotiff
done

Note: The previous script will only move files into a collection without passing properties such as timestamp, which is very important once that you need to filter by date. The following section shows one approach to attach timestamp and some other properties to each file uploaded into a collection.

8. Transfer files from GCS into GEE adding properties to the files

To accomplish this, the files to be uploaded must have information embedded in the filename, such as the date.

Save this script as a bash script and put it in the folder where you have all the files that were transferred.

# ******************** Bash script ********************************************#
#!/bin/bash
# Define static variables
gcBucket="bucket_climate/monthly/tmean/"
imgCol="users/gponce/usda_ars/image_collections/climate/tmean/"
# Create asset in GEE
earthengine create collection $imgCol
strProv="(string)provider=USDA-ARS-SWRC"
# Get file names to extract date and call ingestion command for each file to be added into an asset as image collection
# Example of filenames used here are from a monthly timeseries: rainfall_US_20140101.tif
for file in *.tif; do
    var=$file                       # Get the file name
    yr=${var:12:4}                  # Extract year from the filename, to get this index use the filename and check the index for the year within the filename.  0-based-index
    mon=${var:16:2}                 # Same for extracting month
    vdate=$yr"-"$mon"-01T12:00:00"  # Concatenate strings to get a valid date format to use in the data ingestion co:mmand
    # Remove filename extension ".tif"
    name=$(echo $file | cut -f 1 -d '.')
    asset=$imgCol$name
    # For testing before running, use the following echo statament to print out the final command
    #echo earthengine upload image --asset_id="${asset}" --pyramiding_policy=sample --time_start="${vdate}" --property="\x22${strProv}\x22" --nodata_value=-9999 gs://$gcBucket$file
    # Call the ingestion command with the corresponding parameters to populate properties at each image ingested
    earthengine upload image --asset_id="${asset}" --pyramiding_policy=sample --time_start="${vdate}" --property="${strProv}" --nodata_value=-9999 gs://$gcBucket$file
done

Once the files are uploaded into GEE you can find the image collection at the path you specified above or you can go use to JS code editor and type something like: var my_image_collecton = ee.ImageCollection('users/my_user/climate/image_collections/tmean') print (my_image_collection)

9. Uploading NEON RGB-10cm images

Example of how to upload the RGB Camera 10cm images

Santa Rita Experimental Range

Once the RGB-10cm image files from NEON server are transferred into local server the uploading process can be started sending single images into Google Cloud as follows.

# ******************** Command Line Instructions ******************************#
# Move to folder with images 
cd ~/DATA/NEON_RGB_10cm/
# Run gsutil to copy files into GCS bucket
gsutil -m cp *.tif gs://gee_neon/

That instruction will transfer all the tif files from local drive into GCS bucket.

Setup GEE Image collection for RGB-10cm

Note: Save the following bash block in a file and put it in the same folder as the RGB images.

The following block loop through tif files in the local server to get the file names. Then, those filenames are passed to the earthengine tool to grab each file from the GCS bucket and transfer it to GEE as an asset, each task started with this process can be monitored in the GEE-JS code editor in the tab tasks.

# ******************** Bash script ********************************************#
#!/bin/bash
imgFolder="users/gponce/usda_ars/image_collections/neon_srer_2017_rgb"
# First time, create collection
earthengine create collection $imgFolder
# Bucket in GCS
gcBucket=“gee_neon“
# Create asset in GEE
for file in *.tif; do
    # Remove .tif 
    name=$(echo $file | cut -f 1 -d '.')
    asset=$imgFolder$name
    # For testing before running, use the following echo statament to print out the final command
    #echo earthengine upload image --asset_id="${asset}" --pyramiding_policy=sample gs://$gcBucket$file
    # Call the ingestion command with the corresponding parameters to populate properties at each image ingested
    earthengine upload image --asset_id="${asset}" --pyramiding_policy=sample gs://$gcBucket$file
done

Once this process is done, the RGB-10cm are available as part of an ImageCollection that can be found in the tab Assets in the JS Code editor.

10. Uploading NEON Hyperspectral

Hyperspectral data from NEON is delivered in HDF5 format. To upload data into GEE is required to convert these files into GeoTif format.

These are the general steps to move data from local drive into GEE.

  1. Extract bands (426) from each one of the flight-line files (.h5) and save as geotif (check storage before running this step)
  2. Merge bands back (tif) into single multi-band GeoTif
  3. Upload each multi-band geotif into GEE (Local –> GCS –> GEE)

10.1 Extract bands (426) from each one of the flight-line files (.h5).

# ******************** R script ***********************************************#
# Load R-libraries
library(raster)
library(rhdf5)
library(rgdal)
library(maps)
library(unixtools)

# NOTES on rhdf5
# This installation worked:

# if (!requireNamespace("BiocManager", quietly = TRUE))
#    install.packages("BiocManager")
# BiocManager::install("rhdf5", version = "3.8")

# Setup env. variables
rasterOptions(format = 'GTiff', overwrite = TRUE, 
              tmpdir = '~/REMOTE_SENSING/NEON/R_TMP/', 
              maxmemory=1e+9, chunksize=1e+5, datatype='INT2U', progress='text')

# --------- Global Variables --------------------------
# f <- 'NEON_D14_SRER_DP1_20170829_172625_reflectance.h5'
#args <- commandArgs(trailingOnly = TRUE)
#file <- 'NEON_D14_SRER_DP1_20170829_190359_reflectance.h5' #args[1]

# The following variables contain the structure of the H5 format, you can use
# HDFView software to make sure the structure of the h5 files remains the same
# and also to change it to the right site name, e.g. "SRER".
csr <- "/SRER/Reflectance/Metadata/Coordinate_System/"
spInf <- "/SRER/Reflectance/Metadata/"
sr <- "/SRER/"
reflAttr <- '/SRER/Reflectance/Reflectance_Data'

# --------- Functions ---------------------------------
ConvertBand2Raster <- function(band, f){
  # Converts single band into a tif
  #
  # Args: 
  #  band: numeric value with the band to process
  #  f: string with the hdf filename
  # 
  # Returns: 
  #  a matrix containing the reflectance data for the specific    band
  
  
  mapInfo <- h5read(f,paste0(csr,"Map_Info"))    # csr is global var.
  mapInfo <- noquote(unlist(strsplit(mapInfo,",")))
  
  my_crs <- paste0('+init=epsg:',h5read(f,paste0(csr,"EPSG Code")))
  my_res <- as.numeric(mapInfo[2])
  
  xMin <- as.numeric(mapInfo[4])
  yMax <- as.numeric(mapInfo[5])
  
  reflInfo <- h5readAttributes(f, reflAttr)  # reflAttr is a global variable
  
  # R get attributes for the Reflectance dataset
  nRows <- reflInfo$Dimensions[1]  #reflInfo$row_col_band[1]
  nCols <- reflInfo$Dimensions[2]  #reflInfo$row_col_band[2]
  nBands <- reflInfo$Dimensions[3]  #reflInfo$row_col_band[3]
  scaleFactor <- reflInfo$Scale_Factor
  
  #grab the no data value
  myNoDataValue <- reflInfo$Data_Ignore_Value
  
  
  H5close()
  # Read single band into a array
  out<- h5read(f,reflAttr,index=list(band, 1:nCols,1:nRows))
  
  # Convert from array to matrix
  out <- (out[1,,])
  
  # Transpose data, by using SRER-2017 H5 files this is needed
  out <-t(out)
  
  # Set no-data value 
  out[out == myNoDataValue] <- NA
  
  # Turn matrix into a raster with crs
  outr <- raster(out,crs=my_crs)
  
  # Set variables with extent for the raster based on the Reflectance_Data attributes
  xMin <- reflInfo$Spatial_Extent_meters[1]
  xMax <- reflInfo$Spatial_Extent_meters[2]
  yMin <- reflInfo$Spatial_Extent_meters[3]
  yMax <- reflInfo$Spatial_Extent_meters[4]
  
  # Create extent
  rasExt  <- extent(xMin,xMax,yMin,yMax)
  
  # Set extent to raster
  extent(outr) <- rasExt
  H5close()
  # Return raster object
  
  return(outr)
}


# Set writing and files location
#writePath <- "~/REMOTE_SENSING/NEON/SRER/CAMPAIGN_2017/SPECTROMETER/REFLECTANCE/NEON_TMP/"
# Get all the h5 file name references into a list
# Get the full path
files <- list.files(path="/fullPath/NEON_WGEW_2018/L3/Spectrometer/Reflectance", pattern="*.h5", full.names=T, recursive=FALSE)
# Get the index from the filename e.g: For the file /fullPath/NEON_D14_WGEW_DP3_608000_3515000.h5 
p <- files[1]
# This index is needed to create the tif files 
f_index <- unlist(gregexpr("NEON_D",p))[1]



# Loop through each h5 file and extract and save its bands as GeoTif 
#setwd('~/DATA/NEON_DATA/REFLECTANCE/')
for (fi in files) {
  v_bands <- list(1:426)
  print(paste0("Processing file ", fi))
  ptm <- proc.time()
  v_rast <- lapply(1:426, ConvertBand2Raster, f = fi)
  proc.time() - ptm
  v_stack <- stack(v_rast)
  v_band_names <- paste("Band_", unlist(v_bands),sep="")
  names(v_stack) <- v_band_names
  
  for (i in unlist(v_bands)) {
    r_name <- paste0(substr(gsub(pattern="\\.h5","",fi),
                            f_index,nchar(fi)),
                     '_band_', sprintf("%03d",i),'.tif')
    print(paste0("Processing file ", r_name))
    writeRaster(v_stack[[i]], filename=r_name, format="GTiff")
  }
  removeTmpFiles(h=0)
  gc()
}

At this point there is a single GeoTif per band-file stored.

The next steps are performed in bash to organize the files from each band and prepare the multiband GeoTifs that will be ingested into GEE.

10.2 Merge bands back (tif) into single multi-band GeoTif

# ******************** Command Line Instructions ******************************#
# Do this by date, the following is just an example of a single line and the 
# the file naming changes depending on the site, this case is from SRER site.
# e.g. 
ls NEON_D14_SRER_DP1_20170824_160546_reflectance_band_*.tif > btif.txt  
echo cat btif.txt > processh5.sh
vi processh5.sh

# This is the one-line instruction in Redhat command line, should work in any 
# linux flavor. Change "NEON_D14_SRER_DP3" section for the corresponding site
find -type f -name '*.tif' | awk 'BEGIN{FS="_"}{ print "NEON_D14_SRER_DP3_"$5"_"$6"_reflectance_band_*.tif"}' | sort | unique > btif.txt
echo cat btif.txt > processh5.sh

# The previous on-liner is what you need to run for all the files

# To make the previous command for several files open the file `processh5.sh 
# in `vi` and run the following commands:

# Insert `echo` starting at each row 
:%s /^/echo /g 
# Insert ` >> alltifs.txt` at the end of rows
%s /$/ >> alltifs.txt/g 

# Move to the first row of the file(processh5.sh) and change `>>` by this `>` to create instead of `>>` which is for appending.

# You should now see something like this in the file:  
echo NEON_D14_SRER_DP1_20170824_161655_reflectance_band_*.tif > alltifs.txt
echo NEON_D14_SRER_DP1_20170824_162407_reflectance_band_*.tif >> alltifs.txt
echo NEON_D14_SRER_DP1_20170824_163106_reflectance_band_*.tif >> alltifs.txt
echo NEON_D14_SRER_DP1_20170824_164049_reflectance_band_*.tif >> alltifs.txt
echo NEON_D14_SRER_DP1_20170824_164453_reflectance_band_*.tif >> alltifs.txt
...

# and so on, for each FILE-DATE…
# Close and Run the file `processh5.sh`
chmod +x processh5.sh
./processh5.sh

# After running `processh5.sh` you should get `alltifs.txt` file, then join all 
# the filenames into a single line.

# Using `awk` ;-) 
cat alltifs.txt | awk '{print}' ORS=' ' > all_bands_files

# The `all_bands_files` is used with gdal tools to create mosaics

In the following block, GDAL Tools (gdalbuildvrt) are used to create a virtual file and then generate the final multiband file.

# The goal of this script is to work with GDAL tools `gdalbuildvrt` and 
# `gdal_translate`. With these tools is possible to create a multi-band file.  
# 
# ******************** Command Line Instructions ******************************#
# Insert the following line in the bash file. Where `all_bands_files` is a one line
# text file with all the file names of the tif files for each band in a single line 
# for each date it is required to get one call to `gdalbuildvrt.

# Again, this is just a single example and you don't have to run this...
gdalbuildvrt -separate NEON_WGEW_2018.vrt NEON_FILE_NAME_DATE.tif  all_bands_files

# For SRER 2018 these one-liners were used
# This is used to build the calls to `gdalbuildvrt`
# This one-liner can read as: List all the 'tif' files, then subtract a portion 
# of the filename to create the .vrt file, then pre-append to each .vrt file the 
# instruction `gdalbuildvrt -separate` and send the results into 
# `gdalbuild_first_part.txt`  Don't forget to change the file name... 
find -type f -name '*.tif' | awk 'BEGIN{FS="_"}{ print "NEON_D14_SRER_DP3_"$5"_"$6"_reflectance_band_*.tif"}' | sort | uniq | awk '{print "gdalbuildvrt -separate "substr($0,0,32)".vrt "}' > gdalbuild_first_part.txt

# The output should have as many lines as the number of `.h5` files.  
# Something like: gdalbuildvrt -separate NEON_D14_SRER_DP3_608000_3515000.vrt

# Now, create an index to make a join later
awk '{printf ("%.3d %s\n", NR, $0) }' alltifs.txt > alltifs2.txt

# Create another index
awk '{printf ("%.3d %s\n", NR, $0) }' gdalbuild_first_part.txt  > gdal_temp.txt

# Create one-liner for gdalbuildvrt, this is done to achieve the full one-line 
# gdalbuildvrt instruction.
join -1 1 -2 1 gdal_temp.txt alltifs2.txt > one-liner.sh

# Remove index column from the resulting join
# Since the joined column start like this: 001 NEON...
# The first 4 characters should be removed, therefore use this
sed 's/^.\{4\}//' one-liner.sh  > one-liner2.sh

# one-liner.sh will contain the full instruction to create all the vrt files
# Run one-liner2.sh to get `vrt` files
chmod +x one-liner2.sh
./one-liner2.sh

# The next command is to use the virtual files and generate the multi-band file
# the options were provided by the GEE data ingestion team.

# Repeat the following instruction as many times as different multi-bands will 
# be created. See the command line used to generate a file with this instruction
# on each `vrt` file.

gdal_translate --config GDAL_CACHEMAX 1024 -of GTiff -co COMPRESS=DEFLATE -co ZLEVEL=9 -co BIGTIFF=IF_SAFER -co INTERLEAVE=BAND -co NUM_THREADS=60 NEON_FILE_NAME_DATE.vrt NEON_D14_SRER_D...mb.tif

# A one-liner to create a "bash" for running over all the `vrt` files 
# could be something like this:
find ./ -maxdepth 1 -name "*.vrt"  | sed 's/\.vrt$//' | sed 's/\.\///' | awk '{print "gdal_translate --config GDAL_CACHEMAX 1024 -of GTiff -co COMPRESS=DEFLATE -co ZLEVEL=9 -co BIGTIFF=IF_SAFER -co INTERLEAVE=BAND -co NUM_THREADS=60 "$0".vrt "$0"_reflectance_mb.tif"}' > create_multi_band_files.sh

# Run the resulting file as bash script:
chmod +x create_multi_band_files.sh
./create_multi_band_files.sh

# At this point, you should have a list of `tif` files where each file should 
# have the 426 bands.  In total, for WGEW 2018 survey, 369 files (.h5) were produced.

The gdal_translate is the last step in creating multiband files as GeoTif. After this step the output files are ready to be sent to Google Cloud Storage bucket and then transferred into GEE.

10.3 Upload each multi-band geotif into GEE (Local –> GCS –> GEE)

Start by moving multiband files to GCS. Using the gsutil tool copy the multi-band geotif files into the GCS-bucket.

# The bucket/folder structure below was created in the Google Cloud Console website

gsutil cp *mb.tif gs://gee_neon_srer_2018/L3/Spectrometer/Reflectances/

Once files are in the GCS bucket the following step is to move those files into GEE as an image collection. For doing this, save the following bash script in a file and run it in unix where you are authenticated to google cloud and earthengine.


# ******************** Bash script ********************************************#
#!/bin/bash
imgFolder="users/gponce/usda_ars/image_collections/neon/srer_2018/neon_srer_2018_hs"
# First time, create collection
earthengine create collection $imgFolder
# Bucket in GCS
imgFolder=$imgFolder"/"
strProv="(string)provider=BATTELLE_NEON_SRER_2018"
gcBucket="gee_neon_srer_2018/L3/Spectrometer/Reflectances/"
vdate="2018-08-30T12:00:00"
# Create asset in GEE
for file in *_mb.tif; do
    # Remove .tif 
    name=$(echo $file | cut -f 1 -d '.')
    asset=$imgFolder$name
    # For testing before running, use the following echo statament to print out 
    # the final command.
    
    #echo earthengine upload image --asset_id="${asset}" --time_start="${vdate}" --property="${strProv}" --pyramiding_policy=sample gs://$gcBucket$file
    
    # Call the ingestion command with the corresponding parameters to populate properties at each image ingested
    earthengine upload image --asset_id="${asset}" --time_start="${vdate}" --property="${strProv}" --pyramiding_policy=sample gs://$gcBucket$file
done

This concludes the process for uploading spectrometer reflectances from NEON into Google Earth Engine.

LS0tCnRpdGxlOiAiTm90ZXMgb24gdXBsb2FkaW5nIHZlY3RvciBhbmQgcmFzdGVyIGRhdGEgaW50byBHb29nbGUgRWFydGggRW5naW5lIChHRUUpIgphdXRob3I6IHwKIHwgR3VpbGxlcm1vIEUuIFBvbmNlLUNhbXBvcwogfCBVU0RBLUFSUyBTb3V0aHdlc3QgV2F0ZXJzaGVkIFJlc2VhcmNoIENlbnRlcgogfCBFLW1haWw6IGd1aWxsZXJtby5wb25jZUBhcnMudXNkYS5nb3YKIHwKZGF0ZTogJ1VwZGF0ZWQ6IGByIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVCICVkLCAlWSIpYCcKdGFnczogW0dvb2dsZSwgRWFydGgsIEVuZ2luZSwgUiwgUHl0aG9uLCBORU9OLCBnZW9zcGF0aWFsXQpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogam91cm5hbCAgCmVkaXRvcl9vcHRpb25zOiAgICAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKVGhpcyBub3RlYm9vayBpcyB1c2VkIHRvIGRvY3VtZW50IHRoZSBzdGVwcyBmb3IgdXBsb2FkaW5nIGdlb3NwYXRpYWwgZGF0YSBpbnRvIApHb29nbGUgRWFydGggRW5naW5lIChHRUUpLiBUaGVzZSBzdGVwcyBjb3VsZCBjaGFuZ2Ugb3IgdmFyeSBkZXBlbmRpbmcgb24gdGhlIApuZWVkcyBvZiB0aGUgZW5kLXVzZXIuIEdFRSBhbHJlYWR5IHByb3ZpZGVzIGFuIGV4dGVuc2l2ZSBjYXRhbG9nIG9mIHB1YmxpYyAKZGF0YXNldHMuICBUaGlzIGNhdGFsb2cga2VlcHMgZ3Jvd2luZyBiYXNlZCBvbiB1c2Vycycgc3VnZ2VzdGlvbnMgYnkgdXNpbmcgYW4gCmlzc3VlIHRyYWNraW5nIHN5c3RlbSBjYWxsZWQgW0lzc3VlIFRyYWNrZXIuXShodHRwczovL2lzc3VldHJhY2tlci5nb29nbGUuY29tL2lzc3Vlcz9xPWNvbXBvbmVudGlkOjE4NDQyNiUyMGlzOm9wZW4pCjxicj4gCjxicj4KIVtdKGltYWdlcy9IU19uZW9uMjAxN19TUkVSX3NtYWxsXzEuZ2lmKSAKKioqQW5pbWF0aW9uIHNob3dpbmcgaHlwZXJzcGVjdHJhbCBkYXRhIHZpc3VhbGl6YXRpb24gaW4gR0VFIGZyb20gdGhlIE5FT04gMjAxNyBDYW1wYWlnbiBhdCBTYW50YSBSaXRhIEV4cGVyaW1lbnRhbCBSYW5nZS4qKiogQWNjZXNzIHRvIHRoaXMgaW4gR0VFIGJyb3dzZXIgQVBJOiBodHRwOi8vYml0Lmx5L2h5cGVyX3NyZXJfMjAxNwoKTGVmdCBwYW5lbCBzaG93cyBwbG90cyB3aXRoIHRoZSBzcGVjdHJhbCBzaWduYXR1cmUgcHJvZmlsZSBvZiB0aGUgNDI2IGJhbmRzIGZvciAKYW55IGdpdmVuIHBpeGVsLiBPdGhlciBsYXllcnMgc2hvd24gYXJlIFJHQi1DYW1lcmEgMTBjbSBwaXhlbCBzaXplLCBhbmQgZnJvbQoxbSBoeXBlcnNwZWN0cmFsIGRhdGEgYSBGYWxzZSBjb2xvci1OSVIsIE5hdHVyYWwgQ29sb3IsIGFuZCBORFZJLgogICAKVGhlIHRhc2tzIGRlc2NyaWJlZCBiZWxvdyBhcmUgcGVyZm9ybWVkIHVzaW5nIHRoZSBzZXJ2ZXIgYFNUT1JNYCBAVVNEQS1BUlMtU1dSQy4gCiBUaGVzZSBhcmUgdGhlIHNwZWNpZmljYXRpb25zIG9mIHRoZSBzZXJ2ZXIgYXMgTWF5LzIwMTggYWxvbmcgd2l0aCB0aGUgb3BlcmF0aW5nCnN5c3RlbSBhbmQgc29mdHdhcmUgdXNlZDoKCmBTZXJ2ZXI6IERlbGwgUjkzMCAoc3Rvcm0pYDxicj4KYE8uUy46IFJlZEhhdCA3LjUgKDMuMTAuMC04NjIuNi4zLmVsNy54ODZfNjQpYDxicj4KYFB5dGhvbiB2ZXIuOiAzLjY0YDxicj4KYEdvb2dsZSBDbG91ZCBTREsgdmVyLjogNC4yOGAgKFN0YWdpbmcgcGxhdGZvcm0pPGJyPiAKYEdvb2dsZSBQeXRob24gQVBJIGNsaWVudCBsaWJyYXJ5YCAoUHl0aG9uIGZ1bmN0aW9ucyBieSBHb29nbGUpPGJyPgpgR29vZ2xlIEVhcnRoZW5naW5lIEFQSWAgKFB5dGhvbiBmdW5jdGlvbnMgdG8gYWNjZXNzIEdFRSkgPGJyPgoKIyMjIDEuIEluc3RhbGxhdGlvbiBvZiBHb29nbGUgQ2xvdWQgU29mdHdhcmUgRGV2ZWxvcG1lbnQgS2l0IChTREspCgpHb29nbGUgQ2xvdWQgU3RvcmFnZSBpcyBuZWVkZWQgdG8gc2VydmUgYXMgYSBgc3RhZ2luZyBwbGF0Zm9ybWAgdG8gbW92ZSBkYXRhIGludG8KR0VFLgoKWW91IGNhbiBnZXQgU0RLIGZyb20gaGVyZTogaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL3Nkay9kb2NzL3F1aWNrc3RhcnQtbGludXgKCmBgYHtiYXNofQojICoqKioqKioqKioqKioqKioqKioqIENvbW1hbmQgTGluZSBJbnN0cnVjdGlvbnMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIwojIFVuemlwIGdjIHNkayAKCnRhciAteHZmIGdvb2dsZS1jbG91ZC1zZGsteHh4LjAuMC1saW51eC14ODZfNjQudGFyLmd6ICAgIAoKIyBJbnN0YWxsIGl0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKLi9nb29nbGUtY2xvdWQtc2RrL2luc3RhbGwuc2ggICAgICAgICAgCgogIyBSZWZyZXNoIGVudmlyb25tZW50IHdpdGhvdXQgZG9pbmcgbG9nb3V0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAouIH4vLmJhc2hyYyAgICAgICAgICAKCmBgYAoKIyMjIDIuIEluc3RhbGxhdGlvbiBvZiBgZ29vZ2xlLWFwaS1weXRob24tY2xpZW50YCBhbmQgYGVhcnRoZW5naW5lLWFwaWAuCgpUaGlzIHNldHVwIHdpbGwgZW5hYmxlIHlvdXIgc3lzdGVtIHRvIGFjY2VzcyBHRUUgcGxhdGZvcm0gZnJvbSAKUHl0aG9uIChgaW1wb3J0IGVlYCkgYW5kIGFsc28gdXNlIHRoZSBgZWFydGhlbmdpbmVgIGNvbW1hbmQgbGluZSB0b29sIHRvIAptb3ZlIGRhdGEgZnJvbSBHb29nbGUgQ2xvdWQgaW50byBHRUUuCgpgYGB7YmFzaH0KIyAqKioqKioqKioqKioqKioqKioqKiBDb21tYW5kIExpbmUgSW5zdHJ1Y3Rpb25zICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiMKIyBJbnN0YWxsaW5nIEdvb2dsZSBBUEktUHl0aG9uLWNsaWVudCBsaWJyYXJ5CnN1ZG8gcGlwIGluc3RhbGwgZ29vZ2xlLWFwaS1weXRob24tY2xpZW50CgojIEVuc3VyZSBjcnlwdG8gbGlicmFyeSBpcyBhdmFpbGFibGUuIE5vIGVycm9yIGlzIGV4cGVjdGVkIGFmdGVyIHJ1bm5pbmcgdGhlIGZvbGxvd2luZzoKcHl0aG9uIC1jICJmcm9tIG9hdXRoMmNsaWVudCBpbXBvcnQgY3J5cHQiCgojIElmIG5vIGVycm9yLCBjb250aW51ZSB3aXRoIHRoZSBJbnN0YWxsYXRpb24gb2YgRWFydGggRW5naW5lIFB5dGhvbiBBUEkKCnN1ZG8gcGlwIGluc3RhbGwgZWFydGhlbmdpbmUtYXBpCgojIFN3aXRjaCB0aGUgZXhwYW5kZWQgZGlyZWN0b3J5IGFuZCBydW4gc2NyaXB0CmNkIGVhcnRoZW5naW5lLWFwaS1WRVJTSU9OCnB5dGhvbiBzZXR1cC5weSBpbnN0YWxsCgojIEF1dGhlbnRpY2F0ZSBpbnRvIEdFRSBmcm9tIHRlcm1pbmFsLiBUaGUgZm9sbG93aW5nIGxpbmUgc2hvdWxkIHNob3cgYSBVUkwgCiMgdGhhdCB5b3UgY2FuIHBhc3RlIGludG8gYSBicm93c2VyIHdpbmRvdyB0byBhdXRoZW50aWNhdGUgd2l0aCB5b3VyIGFjY291bnQKIyB0aGF0IGhhcyBhY2Nlc3MgdG8gR0VFLgpweXRob24gLWMgImltcG9ydCBlZTsgZWUuSW5pdGlhbGl6ZSgpIgoKCmBgYApBZnRlciBwcmV2aW91cyBzdGVwcywgR0VFIEFQSSBzaG91bGQgYmUgcmVhZHkgdG8gdXNlIGl0LgoKIyMjIDMuIE1vdmluZyBkYXRhIGZyb20gYSBsb2NhbCBkcml2ZSBpbnRvIEdFRQpUaGUgd29ya2Zsb3cgdG8gbW92ZSBkYXRhIGlzOgoKKipMb2NhbERyaXZlIC0tPiBHb29nbGUgQ2xvdWQgU3RvcmFnZSAoYnVja2V0KSAgLS0+IEdFRSAoYXNzZXQpKioKCk9uZSBvZiB0aGUgY3VycmVudCBsaW1pdGF0aW9ucyBvbiB1cGxvYWRpbmcgc2V2ZXJhbCBmaWxlcyBpbnRvIEdFRSBpcyB0aGF0IHlvdQptdXN0IHVzZSBHb29nbGUgQ2xvdWQgU3RvcmFnZShHQ1MpIGFzIHRoZSBzdGFnaW5nIHBsYXRmb3JtLiAKKipZb3UgY2FuIGFsc28gdXBsb2FkIGRhdGEgdXNpbmcgdGhlIEphdmFTY3JpcHQgQ29kZSBFZGl0b3IqKiwgaG93ZXZlciwgdGhpcyBhcHByb2FjaCAKd2lsbCBvbmx5IGFsbG93IHlvdSB0byB1cGxvYWQgc2luZ2xlIGZpbGVzLCBvbmUgYnkgb25lIGluc3RlYWQgb2YgdXNpbmcgYSBiYXRjaApwcm9jZXNzLiBJbiB0aGUgaW1hZ2UgYmVsb3c6CgotIGBJbWFnZSBVcGxvYWRgIHRvIHVwbG9hZCBzaW5nbGUgaW1hZ2UvcmFzdGVyCi0gYFRhYmxlIHVwbG9hZGAgdG8gdXBsb2FkIGEgdmVjdG9yIChzaGFwZWZpbGUpLCB5b3UgbmVlZCB0byBzZWxlY3QgYWxsIHRoZSAKc2hwIHN0YW5kYXJkIGZpbGVzICguc2hwLCAuc2h4LCAuZGJmLCAucHJqKS4KLSBgSW1hZ2UgY29sbGVjdGlvbmAgdG8gdXBsb2FkIHNldmVyYWwgaW1hZ2VzIGFzIHBhcnQgb2YgYSBjb2xsZWN0aW9uIChlLmcuIAp0aW1lc2VyaWVzLCBtdWx0aWJhbmQvdGltZXNlcmllcykuCi0gYEZvbGRlcmAgdG8gY3JlYXRlIGEgZm9sZGVyIGluIHlvdXIgcGVyc29uYWwgcGF0aC9kaXJlY3RvcnkuCgohW10oaW1hZ2VzL1VwbG9hZENvZGVFZGl0b3IucG5nKQoKVXNpbmcgdGhlIGBUYWJsZSB1cGxvYWRgIGlzIHRoZSByaWdodCBvcHRpb24gdG8gdXBsb2FkIHZlY3RvciBkYXRhIGFzIHNoYXBlZmlsZXMuCgpEYXRhc2V0cyBpbiBHRUUgYXJlIGNhbGxlZCBhc3NldHMgYW5kIHRvIHVwbG9hZCBpbnRvIGFzc2V0cyB3ZSBuZWVkIHRvIApjcmVhdGUgdGhlIGZvbGRlciBzdHJ1Y3R1cmUgYXMgbmVlZGVkLgoKQWxsIHRoZSBkYXRhc2V0cyBpbiBHRUUgYXJlIGNhbGxlZCBhc3NldHM6IGBJbWFnZXMsIEltYWdlIENvbGxlY3Rpb25zLCBhbmQgVGFibGVzKHNocCBmaWxlcylgLgoKVG8gc3RhcnQgd29ya2luZyB3aXRoIGFzc2V0cyBmcm9tIHRoZSBjb21tYW5kIGxpbmUgd2UgbmVlZCB0byB1c2UgdGhlIGNvbW1hbmQgbGluZSAoQ0xJKSAKYGVhcnRoZW5naW5lYCAoYXZhaWxhYmxlIGFzIHBhcnQgb2YgdGhlIEdFRSBBUEkgaW5zdGFsbGF0aW9uKS4gIAoKIyMjIDQuIEF1dGhlbnRpY2F0aW9uIHRvIEdvb2dsZSBDbG91ZAoKVGhlc2Ugc3RlcHMgd2lsbCBsZXQgeW91IGF1dGhlbnRpY2F0ZSBpbnRvIEdvb2dsZSBDbG91ZC4gV2UgbmVlZCB0aGlzIGFzIGEgCnN0YWdpbmcgcGxhdGZvcm0gYmV0d2VlbiBsb2NhbCBkcml2ZSBhbmQgR0VFLgoKCmBgYHtiYXNofSAKIyAqKioqKioqKioqKioqKioqKioqKiBDb21tYW5kIExpbmUgSW5zdHJ1Y3Rpb25zICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiMKIyBUaGlzIGNvbW1hbmQgd2lsbCB0cnkgZWl0aGVyIHRvIG9wZW4gYSBicm93c2VyIG9yIGRpc3BsYXkgYSBsaW5rIHRoYXQgeW91IGNhbgojIHBhc3RlIGludG8gYnJvd3NlciB3aW5kb3cuCmdjbG91ZCBhdXRoIGxvZ2luCgojIFNldCBwcm9qZWN0IElEIGFzIGNyZWF0ZWQgYXQgaHR0cHM6Ly9jb25zb2xlLmNsb3VkLmdvb2dsZS5jb20vc3RvcmFnZS8KZ2Nsb3VkIGNvbmZpZyBzZXQgcHJvamVjdCB4eHh4LXl5eXktMTcyMTE2CgojIENyZWF0ZSBhIGJ1Y2tldCwgYSBmb2xkZXIgZm9yIGRhdGEgc3RhZ2luZwpnc3V0aWwgbWIgZ3M6Ly9teV9idWNrZXQKCiMgbGlzdCBidWNrZXRzIHlvdSBoYXZlIGluIHlvdXIgcHJvamVjdApnc3V0aWwgbHMgZ3M6Ly8KCmBgYAoKIyMjIDUuIFVwbG9hZGluZyBmaWxlcyBmcm9tIGxvY2FsIGRpc2sgaW50byBHb29nbGUgQ2xvdWQKCkEgc2luZ2xlIGNvcHkgY2FuIGJlIG1hZGUgZnJvbSB0aGUgY29tbWFuZCBsaW5lIG9uY2UgeW91IGFyZSBhdXRoZW50aWNhdGVkIAppbnRvIEdvb2dsZSBDbG91ZC4KCmBgYHtiYXNofQojICoqKioqKioqKioqKioqKioqKioqIENvbW1hbmQgTGluZSBJbnN0cnVjdGlvbnMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIwpnc3V0aWwgLW0gY3AgKi50aWYgZ3M6Ly9teV9idWNrZXQvZm9sZGVyX0EvCgpgYGAKVGhpcyB3aWxsIGxhdW5jaCBvbmUgdGFzayBwZXIgZmlsZSBhbmQgd2lsbCBydW4gaW4gcGFyYWxsZWwgYnkgdXNpbmcgYXZhaWxhYmxlCmNvcmVzLiAKCiMjIyA2LiBVc2luZyBgZWFydGhlbmdpbmVgIGNvbW1hbmQgdG8gY3JlYXRlIGZvbGRlcnMvaW1hZ2UgY29sbGVjdGlvbnMKCkJlZm9yZSBtb3ZpbmcgZGF0YSBpbnRvIEdFRSB5b3Ugc2hvdWxkIGNyZWF0ZSBhIHRhcmdldCBmb2xkZXIvaW1hZ2UgY29sbGVjdGlvbiBlaXRoZXIgCnVzaW5nIHRoZSBgZWFydGhlbmdpbmVgIENMSSBvciB0aGUgR0VFIEphdmFzY3JpcHQgY29kZSBlZGl0b3IgaHR0cHM6Ly9jb2RlLmVhcnRoZW5naW5lLmdvb2dsZS5jb20KVGhlIGZvbGxvd2luZyBiYXNoIGJsb2NrIHNob3dzIGFuIGV4YW1wbGUgb2YgaG93IHRvIHVzZSB0aGUgQ0xJIGBlYXJ0aGVuZ2luZWAgdG9vbAp0byBjcmVhdGUgYSBmb2xkZXIgYW5kIGFuIGltYWdlIGNvbGxlY3Rpb24uCgpgYGB7YmFzaH0KIyAqKioqKioqKioqKioqKioqKioqKiBDb21tYW5kIExpbmUgSW5zdHJ1Y3Rpb25zICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiMKIyBIZXJlIHdlIGNyZWF0ZSBhIGZvbGRlciBpbiBHRUUgdXNpbmcgZWFydGhlbmdpbmUgdG9vbAplYXJ0aGVuZ2luZSBjcmVhdGUgZm9sZGVyIHVzZXJzL2dwb25jZS91c2RhX2Fycy9zaGFwZWZpbGVzL0FaX0xBTkQKIyBDcmVhdGUgYSBjb2xsZWN0aW9uIChpdCBsb29rcyBsaWtlIGFub3RoZXIgZm9sZGVyLCBidXQgaW50ZXJuYWxseSBpdCB3aWxsIGJlCiMgYW4gaW1hZ2UgY29sbGVjdGlvbikuCmVhcnRoZW5naW5lIGNyZWF0ZSBjb2xsZWN0aW9uIHVzZXJzL215X3VzZXJuYW1lL2NvbGxlY3Rpb25faWQKCmBgYAojIyMgNy4gVHJhbnNmZXIgZmlsZXMgZnJvbSBHQ1MgYnVja2V0IGludG8gR0VFIEFzc2V0CgpUaGUgZm9sbG93aW5nIHNjcmlwdCBjYW4gYmUgc2F2ZWQgYXMgYSBiYXNoIHNjcmlwdCAodHJhbnNmZXJHQzJHRS5zaCkgYW5kIApydW4gaXQgYXM6CmAuL3RyYW5zZmVyR0MyR0Uuc2ggbmFtZV9idWNrZXRfZ2Nsb3VkIG5hbWVfYXNzZXRgCgp3aGVyZToKLSBuYW1lX2J1Y2tldF9nY2xvdWQgOiBuYW1lIG9mIHRoZSBidWNrZXQgY3JlYXRlZCBpbiBHb29nbGUgQ2xvdWQgCi0gbmFtZV9hc3NldDogcmVsYXRpdmUgcGF0aCBpbiBHRUUuIEUuZy4gaWYgeW91IGhhdmUgcGF0aCBsaWtlIApgdXNlcnMvbXlfdXNlci9Gb2xkZXJBYCB5b3Ugc2hvdWxkIG9ubHkgaW5jbHVkZSBgbXlfdXNlci9Gb2xkZXJBYCBhcyB0aGUgCnNlY29uZCBwYXJhbWV0ZXIuCgoKYGBge2Jhc2h9CiMgKioqKioqKioqKioqKioqKioqKiogQmFzaCBzY3JpcHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiojCiMhL3Vzci9iaW4vZW52IGJhc2gKIyBVc2FnZToKIyAuL3RyYW5zZmVyX3RvX2dlZS5zaCBzcmNfYnVja2V0IGRlc3RfYXNzZXQKCnJlc3VsdD1gZWFydGhlbmdpbmUgY3JlYXRlIGNvbGxlY3Rpb24gdXNlcnMvJDJgCmlmIGB0ZXN0IC16ICIkcmVzdWx0ImA7IHRoZW4KICAgIGVjaG8gJHJlc3VsdAogICAgZXhpdCAxCmZpCiMgSW4gdGhlIGZvbGxvd2luZyBsb29wIHdlIGdldCB0aGUgZW50aXJlIHBhdGggdG8gYWxsIHRoZSBnZW90aWZzIHVzaW5nIHRoZSBzcGVjaWZpZWQgCiMgR2Nsb3VkIGJ1Y2tldC4gRWFjaCBmaWxlIHdpbGwgaGF2ZSBhIGZvcm1hdCBsaWtlIHRoaXM6IGdzOi8vbXlfZ2VlX2J1Y2tldC9GSUxFX0phbnVhcnkyMDAwLnRpZgojIEVhY2ggY2FsbCB0byBlYXJ0aGVuZ2luZSBpdCB3aWxsIGxhdW5jaCBhIHRhc2sgdGhhdCB5b3UgY2FuIG1vbml0b3IgaW4gdGhlIEpTIENvZGUgZWRpdG9yIAojIGF0IHRoZSAidGFza3MiIHRhYi4KZm9yIGdlb3RpZmYgaW4gYGdzdXRpbCBscyBnczovLyQxLyoudGlmYDsgZG8gIAogICAgZmlsZW5hbWU9YGJhc2VuYW1lICRnZW90aWZmYAogICAgYXNzZXRfaWQ9IiR7ZmlsZW5hbWUlLip9IgogICAgZWFydGhlbmdpbmUgdXBsb2FkIGltYWdlIC0tYXNzZXRfaWQ9dXNlcnMvJDIvJGFzc2V0X2lkICRnZW90aWZmCmRvbmUKCmBgYAoqKk5vdGU6KiogVGhlIHByZXZpb3VzIHNjcmlwdCB3aWxsIG9ubHkgbW92ZSBmaWxlcyBpbnRvIGEgY29sbGVjdGlvbiB3aXRob3V0IApwYXNzaW5nIHByb3BlcnRpZXMgc3VjaCBhcyB0aW1lc3RhbXAsIHdoaWNoIGlzIHZlcnkgaW1wb3J0YW50IG9uY2UgdGhhdCB5b3UgCm5lZWQgdG8gZmlsdGVyIGJ5IGRhdGUuClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBzaG93cyBvbmUgYXBwcm9hY2ggdG8gYXR0YWNoIHRpbWVzdGFtcCBhbmQgc29tZSBvdGhlciAKcHJvcGVydGllcyB0byBlYWNoIGZpbGUgdXBsb2FkZWQgaW50byBhIGNvbGxlY3Rpb24uCgojIyMgOC4gVHJhbnNmZXIgZmlsZXMgZnJvbSBHQ1MgaW50byBHRUUgYWRkaW5nIHByb3BlcnRpZXMgdG8gdGhlIGZpbGVzCgpUbyBhY2NvbXBsaXNoIHRoaXMsIHRoZSBmaWxlcyB0byBiZSB1cGxvYWRlZCBtdXN0IGhhdmUgaW5mb3JtYXRpb24gZW1iZWRkZWQgaW4gdGhlIApmaWxlbmFtZSwgc3VjaCBhcyB0aGUgZGF0ZS4gCgpTYXZlIHRoaXMgc2NyaXB0IGFzIGEgYmFzaCBzY3JpcHQgYW5kIHB1dCBpdCBpbiB0aGUgZm9sZGVyIHdoZXJlIHlvdSBoYXZlIGFsbCB0aGUgCmZpbGVzIHRoYXQgd2VyZSB0cmFuc2ZlcnJlZC4KYGBge2Jhc2h9CiMgKioqKioqKioqKioqKioqKioqKiogQmFzaCBzY3JpcHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiojCiMhL2Jpbi9iYXNoCiMgRGVmaW5lIHN0YXRpYyB2YXJpYWJsZXMKZ2NCdWNrZXQ9ImJ1Y2tldF9jbGltYXRlL21vbnRobHkvdG1lYW4vIgppbWdDb2w9InVzZXJzL2dwb25jZS91c2RhX2Fycy9pbWFnZV9jb2xsZWN0aW9ucy9jbGltYXRlL3RtZWFuLyIKIyBDcmVhdGUgYXNzZXQgaW4gR0VFCmVhcnRoZW5naW5lIGNyZWF0ZSBjb2xsZWN0aW9uICRpbWdDb2wKc3RyUHJvdj0iKHN0cmluZylwcm92aWRlcj1VU0RBLUFSUy1TV1JDIgojIEdldCBmaWxlIG5hbWVzIHRvIGV4dHJhY3QgZGF0ZSBhbmQgY2FsbCBpbmdlc3Rpb24gY29tbWFuZCBmb3IgZWFjaCBmaWxlIHRvIGJlIGFkZGVkIGludG8gYW4gYXNzZXQgYXMgaW1hZ2UgY29sbGVjdGlvbgojIEV4YW1wbGUgb2YgZmlsZW5hbWVzIHVzZWQgaGVyZSBhcmUgZnJvbSBhIG1vbnRobHkgdGltZXNlcmllczogcmFpbmZhbGxfVVNfMjAxNDAxMDEudGlmCmZvciBmaWxlIGluICoudGlmOyBkbwogICAgdmFyPSRmaWxlICAgICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgZmlsZSBuYW1lCiAgICB5cj0ke3ZhcjoxMjo0fSAgICAgICAgICAgICAgICAgICMgRXh0cmFjdCB5ZWFyIGZyb20gdGhlIGZpbGVuYW1lLCB0byBnZXQgdGhpcyBpbmRleCB1c2UgdGhlIGZpbGVuYW1lIGFuZCBjaGVjayB0aGUgaW5kZXggZm9yIHRoZSB5ZWFyIHdpdGhpbiB0aGUgZmlsZW5hbWUuICAwLWJhc2VkLWluZGV4CiAgICBtb249JHt2YXI6MTY6Mn0gICAgICAgICAgICAgICAgICMgU2FtZSBmb3IgZXh0cmFjdGluZyBtb250aAogICAgdmRhdGU9JHlyIi0iJG1vbiItMDFUMTI6MDA6MDAiICAjIENvbmNhdGVuYXRlIHN0cmluZ3MgdG8gZ2V0IGEgdmFsaWQgZGF0ZSBmb3JtYXQgdG8gdXNlIGluIHRoZSBkYXRhIGluZ2VzdGlvbiBjbzptbWFuZAogICAgIyBSZW1vdmUgZmlsZW5hbWUgZXh0ZW5zaW9uICIudGlmIgogICAgbmFtZT0kKGVjaG8gJGZpbGUgfCBjdXQgLWYgMSAtZCAnLicpCiAgICBhc3NldD0kaW1nQ29sJG5hbWUKICAgICMgRm9yIHRlc3RpbmcgYmVmb3JlIHJ1bm5pbmcsIHVzZSB0aGUgZm9sbG93aW5nIGVjaG8gc3RhdGFtZW50IHRvIHByaW50IG91dCB0aGUgZmluYWwgY29tbWFuZAogICAgI2VjaG8gZWFydGhlbmdpbmUgdXBsb2FkIGltYWdlIC0tYXNzZXRfaWQ9IiR7YXNzZXR9IiAtLXB5cmFtaWRpbmdfcG9saWN5PXNhbXBsZSAtLXRpbWVfc3RhcnQ9IiR7dmRhdGV9IiAtLXByb3BlcnR5PSJceDIyJHtzdHJQcm92fVx4MjIiIC0tbm9kYXRhX3ZhbHVlPS05OTk5IGdzOi8vJGdjQnVja2V0JGZpbGUKICAgICMgQ2FsbCB0aGUgaW5nZXN0aW9uIGNvbW1hbmQgd2l0aCB0aGUgY29ycmVzcG9uZGluZyBwYXJhbWV0ZXJzIHRvIHBvcHVsYXRlIHByb3BlcnRpZXMgYXQgZWFjaCBpbWFnZSBpbmdlc3RlZAogICAgZWFydGhlbmdpbmUgdXBsb2FkIGltYWdlIC0tYXNzZXRfaWQ9IiR7YXNzZXR9IiAtLXB5cmFtaWRpbmdfcG9saWN5PXNhbXBsZSAtLXRpbWVfc3RhcnQ9IiR7dmRhdGV9IiAtLXByb3BlcnR5PSIke3N0clByb3Z9IiAtLW5vZGF0YV92YWx1ZT0tOTk5OSBnczovLyRnY0J1Y2tldCRmaWxlCmRvbmUKCmBgYApPbmNlIHRoZSBmaWxlcyBhcmUgdXBsb2FkZWQgaW50byBHRUUgeW91IGNhbiBmaW5kIHRoZSBpbWFnZSBjb2xsZWN0aW9uIGF0IHRoZSBwYXRoCnlvdSBzcGVjaWZpZWQgYWJvdmUgb3IgeW91IGNhbiBnbyB1c2UgdG8gSlMgY29kZSBlZGl0b3IgYW5kIHR5cGUgc29tZXRoaW5nIGxpa2U6CmB2YXIgbXlfaW1hZ2VfY29sbGVjdG9uID0gZWUuSW1hZ2VDb2xsZWN0aW9uKCd1c2Vycy9teV91c2VyL2NsaW1hdGUvaW1hZ2VfY29sbGVjdGlvbnMvdG1lYW4nKWAKYHByaW50IChteV9pbWFnZV9jb2xsZWN0aW9uKWAgIAoKIyMjIDkuIFVwbG9hZGluZyBORU9OIFJHQi0xMGNtIGltYWdlcwoKKipFeGFtcGxlIG9mIGhvdyB0byB1cGxvYWQgdGhlIFJHQiBDYW1lcmEgMTBjbSBpbWFnZXMqKgoKKioqU2FudGEgUml0YSBFeHBlcmltZW50YWwgUmFuZ2UqKioKCk9uY2UgdGhlIFJHQi0xMGNtIGltYWdlIGZpbGVzIGZyb20gTkVPTiBzZXJ2ZXIgYXJlIHRyYW5zZmVycmVkIGludG8gbG9jYWwgc2VydmVyIHRoZSAKdXBsb2FkaW5nIHByb2Nlc3MgY2FuIGJlIHN0YXJ0ZWQgc2VuZGluZyBzaW5nbGUgaW1hZ2VzIGludG8gR29vZ2xlIENsb3VkIGFzIGZvbGxvd3MuIAoKCmBgYHtiYXNofQojICoqKioqKioqKioqKioqKioqKioqIENvbW1hbmQgTGluZSBJbnN0cnVjdGlvbnMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIwojIE1vdmUgdG8gZm9sZGVyIHdpdGggaW1hZ2VzIApjZCB+L0RBVEEvTkVPTl9SR0JfMTBjbS8KIyBSdW4gZ3N1dGlsIHRvIGNvcHkgZmlsZXMgaW50byBHQ1MgYnVja2V0CmdzdXRpbCAtbSBjcCAqLnRpZiBnczovL2dlZV9uZW9uLwoKYGBgClRoYXQgaW5zdHJ1Y3Rpb24gd2lsbCB0cmFuc2ZlciBhbGwgdGhlIHRpZiBmaWxlcyBmcm9tIGxvY2FsIGRyaXZlIGludG8gR0NTIGJ1Y2tldC4KCioqU2V0dXAgR0VFIEltYWdlIGNvbGxlY3Rpb24gZm9yIFJHQi0xMGNtKioKCioqTm90ZToqKiBTYXZlIHRoZSBmb2xsb3dpbmcgYmFzaCBibG9jayBpbiBhIGZpbGUgYW5kIHB1dCBpdCBpbiB0aGUgc2FtZSBmb2xkZXIgCmFzIHRoZSBSR0IgaW1hZ2VzLgoKVGhlIGZvbGxvd2luZyBibG9jayBsb29wIHRocm91Z2ggdGlmIGZpbGVzIGluIHRoZSBsb2NhbCBzZXJ2ZXIgdG8gZ2V0IHRoZSBmaWxlIApuYW1lcy4gVGhlbiwgdGhvc2UgZmlsZW5hbWVzIGFyZSBwYXNzZWQgdG8gdGhlICBgZWFydGhlbmdpbmVgIHRvb2wgdG8gZ3JhYiBlYWNoIGZpbGUKZnJvbSB0aGUgR0NTIGJ1Y2tldCBhbmQgdHJhbnNmZXIgaXQgdG8gR0VFIGFzIGFuIGFzc2V0LCBlYWNoIHRhc2sgc3RhcnRlZCB3aXRoIHRoaXMgCnByb2Nlc3MgY2FuIGJlIG1vbml0b3JlZCBpbiB0aGUgR0VFLUpTIGNvZGUgZWRpdG9yIGluIHRoZSB0YWIgYHRhc2tzYC4KCmBgYHtiYXNofQojICoqKioqKioqKioqKioqKioqKioqIEJhc2ggc2NyaXB0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIwojIS9iaW4vYmFzaAppbWdGb2xkZXI9InVzZXJzL2dwb25jZS91c2RhX2Fycy9pbWFnZV9jb2xsZWN0aW9ucy9uZW9uX3NyZXJfMjAxN19yZ2IiCiMgRmlyc3QgdGltZSwgY3JlYXRlIGNvbGxlY3Rpb24KZWFydGhlbmdpbmUgY3JlYXRlIGNvbGxlY3Rpb24gJGltZ0ZvbGRlcgojIEJ1Y2tldCBpbiBHQ1MKZ2NCdWNrZXQ94oCcZ2VlX25lb27igJwKIyBDcmVhdGUgYXNzZXQgaW4gR0VFCmZvciBmaWxlIGluICoudGlmOyBkbwogICAgIyBSZW1vdmUgLnRpZiAKICAgIG5hbWU9JChlY2hvICRmaWxlIHwgY3V0IC1mIDEgLWQgJy4nKQogICAgYXNzZXQ9JGltZ0ZvbGRlciRuYW1lCiAgICAjIEZvciB0ZXN0aW5nIGJlZm9yZSBydW5uaW5nLCB1c2UgdGhlIGZvbGxvd2luZyBlY2hvIHN0YXRhbWVudCB0byBwcmludCBvdXQgdGhlIGZpbmFsIGNvbW1hbmQKICAgICNlY2hvIGVhcnRoZW5naW5lIHVwbG9hZCBpbWFnZSAtLWFzc2V0X2lkPSIke2Fzc2V0fSIgLS1weXJhbWlkaW5nX3BvbGljeT1zYW1wbGUgZ3M6Ly8kZ2NCdWNrZXQkZmlsZQogICAgIyBDYWxsIHRoZSBpbmdlc3Rpb24gY29tbWFuZCB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIHBhcmFtZXRlcnMgdG8gcG9wdWxhdGUgcHJvcGVydGllcyBhdCBlYWNoIGltYWdlIGluZ2VzdGVkCiAgICBlYXJ0aGVuZ2luZSB1cGxvYWQgaW1hZ2UgLS1hc3NldF9pZD0iJHthc3NldH0iIC0tcHlyYW1pZGluZ19wb2xpY3k9c2FtcGxlIGdzOi8vJGdjQnVja2V0JGZpbGUKZG9uZQoKYGBgCk9uY2UgdGhpcyBwcm9jZXNzIGlzIGRvbmUsIHRoZSBSR0ItMTBjbSBhcmUgYXZhaWxhYmxlIGFzIHBhcnQgb2YgYW4gSW1hZ2VDb2xsZWN0aW9uCnRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSB0YWIgYEFzc2V0c2AgaW4gdGhlIEpTIENvZGUgZWRpdG9yLgoKIyMjIDEwLiBVcGxvYWRpbmcgTkVPTiBIeXBlcnNwZWN0cmFsCgpIeXBlcnNwZWN0cmFsIGRhdGEgZnJvbSBORU9OIGlzIGRlbGl2ZXJlZCBpbiBIREY1IGZvcm1hdC4gIFRvIHVwbG9hZCBkYXRhIGludG8gR0VFCmlzIHJlcXVpcmVkIHRvIGNvbnZlcnQgdGhlc2UgZmlsZXMgaW50byBHZW9UaWYgZm9ybWF0LiAKClRoZXNlIGFyZSB0aGUgZ2VuZXJhbCBzdGVwcyB0byBtb3ZlIGRhdGEgZnJvbSBsb2NhbCBkcml2ZSBpbnRvIEdFRS4KCjEuIEV4dHJhY3QgYmFuZHMgKDQyNikgZnJvbSBlYWNoIG9uZSBvZiB0aGUgZmxpZ2h0LWxpbmUgZmlsZXMgKC5oNSkgYW5kIHNhdmUgYXMgCmdlb3RpZiAoY2hlY2sgc3RvcmFnZSBiZWZvcmUgcnVubmluZyB0aGlzIHN0ZXApIAoyLiBNZXJnZSBiYW5kcyBiYWNrICh0aWYpIGludG8gc2luZ2xlIG11bHRpLWJhbmQgR2VvVGlmIAozLiBVcGxvYWQgZWFjaCBtdWx0aS1iYW5kIGdlb3RpZiBpbnRvIEdFRSAoTG9jYWwgLS0+IEdDUyAtLT4gR0VFKSA8YnI+PGJyPgoKIyMjIyAxMC4xIEV4dHJhY3QgYmFuZHMgKDQyNikgZnJvbSBlYWNoIG9uZSBvZiB0aGUgZmxpZ2h0LWxpbmUgZmlsZXMgKC5oNSkuCmBgYHtyfQojICoqKioqKioqKioqKioqKioqKioqIFIgc2NyaXB0ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIwojIExvYWQgUi1saWJyYXJpZXMKbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkocmhkZjUpCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkobWFwcykKbGlicmFyeSh1bml4dG9vbHMpCgojIE5PVEVTIG9uIHJoZGY1CiMgVGhpcyBpbnN0YWxsYXRpb24gd29ya2VkOgoKIyBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQojICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgicmhkZjUiLCB2ZXJzaW9uID0gIjMuOCIpCgojIFNldHVwIGVudi4gdmFyaWFibGVzCnJhc3Rlck9wdGlvbnMoZm9ybWF0ID0gJ0dUaWZmJywgb3ZlcndyaXRlID0gVFJVRSwgCiAgICAgICAgICAgICAgdG1wZGlyID0gJ34vUkVNT1RFX1NFTlNJTkcvTkVPTi9SX1RNUC8nLCAKICAgICAgICAgICAgICBtYXhtZW1vcnk9MWUrOSwgY2h1bmtzaXplPTFlKzUsIGRhdGF0eXBlPSdJTlQyVScsIHByb2dyZXNzPSd0ZXh0JykKCiMgLS0tLS0tLS0tIEdsb2JhbCBWYXJpYWJsZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBmIDwtICdORU9OX0QxNF9TUkVSX0RQMV8yMDE3MDgyOV8xNzI2MjVfcmVmbGVjdGFuY2UuaDUnCiNhcmdzIDwtIGNvbW1hbmRBcmdzKHRyYWlsaW5nT25seSA9IFRSVUUpCiNmaWxlIDwtICdORU9OX0QxNF9TUkVSX0RQMV8yMDE3MDgyOV8xOTAzNTlfcmVmbGVjdGFuY2UuaDUnICNhcmdzWzFdCgojIFRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIGNvbnRhaW4gdGhlIHN0cnVjdHVyZSBvZiB0aGUgSDUgZm9ybWF0LCB5b3UgY2FuIHVzZQojIEhERlZpZXcgc29mdHdhcmUgdG8gbWFrZSBzdXJlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGg1IGZpbGVzIHJlbWFpbnMgdGhlIHNhbWUKIyBhbmQgYWxzbyB0byBjaGFuZ2UgaXQgdG8gdGhlIHJpZ2h0IHNpdGUgbmFtZSwgZS5nLiAiU1JFUiIuCmNzciA8LSAiL1NSRVIvUmVmbGVjdGFuY2UvTWV0YWRhdGEvQ29vcmRpbmF0ZV9TeXN0ZW0vIgpzcEluZiA8LSAiL1NSRVIvUmVmbGVjdGFuY2UvTWV0YWRhdGEvIgpzciA8LSAiL1NSRVIvIgpyZWZsQXR0ciA8LSAnL1NSRVIvUmVmbGVjdGFuY2UvUmVmbGVjdGFuY2VfRGF0YScKCiMgLS0tLS0tLS0tIEZ1bmN0aW9ucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KQ29udmVydEJhbmQyUmFzdGVyIDwtIGZ1bmN0aW9uKGJhbmQsIGYpewogICMgQ29udmVydHMgc2luZ2xlIGJhbmQgaW50byBhIHRpZgogICMKICAjIEFyZ3M6IAogICMgIGJhbmQ6IG51bWVyaWMgdmFsdWUgd2l0aCB0aGUgYmFuZCB0byBwcm9jZXNzCiAgIyAgZjogc3RyaW5nIHdpdGggdGhlIGhkZiBmaWxlbmFtZQogICMgCiAgIyBSZXR1cm5zOiAKICAjICBhIG1hdHJpeCBjb250YWluaW5nIHRoZSByZWZsZWN0YW5jZSBkYXRhIGZvciB0aGUgc3BlY2lmaWMgICAgYmFuZAogIAogIAogIG1hcEluZm8gPC0gaDVyZWFkKGYscGFzdGUwKGNzciwiTWFwX0luZm8iKSkgICAgIyBjc3IgaXMgZ2xvYmFsIHZhci4KICBtYXBJbmZvIDwtIG5vcXVvdGUodW5saXN0KHN0cnNwbGl0KG1hcEluZm8sIiwiKSkpCiAgCiAgbXlfY3JzIDwtIHBhc3RlMCgnK2luaXQ9ZXBzZzonLGg1cmVhZChmLHBhc3RlMChjc3IsIkVQU0cgQ29kZSIpKSkKICBteV9yZXMgPC0gYXMubnVtZXJpYyhtYXBJbmZvWzJdKQogIAogIHhNaW4gPC0gYXMubnVtZXJpYyhtYXBJbmZvWzRdKQogIHlNYXggPC0gYXMubnVtZXJpYyhtYXBJbmZvWzVdKQogIAogIHJlZmxJbmZvIDwtIGg1cmVhZEF0dHJpYnV0ZXMoZiwgcmVmbEF0dHIpICAjIHJlZmxBdHRyIGlzIGEgZ2xvYmFsIHZhcmlhYmxlCiAgCiAgIyBSIGdldCBhdHRyaWJ1dGVzIGZvciB0aGUgUmVmbGVjdGFuY2UgZGF0YXNldAogIG5Sb3dzIDwtIHJlZmxJbmZvJERpbWVuc2lvbnNbMV0gICNyZWZsSW5mbyRyb3dfY29sX2JhbmRbMV0KICBuQ29scyA8LSByZWZsSW5mbyREaW1lbnNpb25zWzJdICAjcmVmbEluZm8kcm93X2NvbF9iYW5kWzJdCiAgbkJhbmRzIDwtIHJlZmxJbmZvJERpbWVuc2lvbnNbM10gICNyZWZsSW5mbyRyb3dfY29sX2JhbmRbM10KICBzY2FsZUZhY3RvciA8LSByZWZsSW5mbyRTY2FsZV9GYWN0b3IKICAKICAjZ3JhYiB0aGUgbm8gZGF0YSB2YWx1ZQogIG15Tm9EYXRhVmFsdWUgPC0gcmVmbEluZm8kRGF0YV9JZ25vcmVfVmFsdWUKICAKICAKICBINWNsb3NlKCkKICAjIFJlYWQgc2luZ2xlIGJhbmQgaW50byBhIGFycmF5CiAgb3V0PC0gaDVyZWFkKGYscmVmbEF0dHIsaW5kZXg9bGlzdChiYW5kLCAxOm5Db2xzLDE6blJvd3MpKQogIAogICMgQ29udmVydCBmcm9tIGFycmF5IHRvIG1hdHJpeAogIG91dCA8LSAob3V0WzEsLF0pCiAgCiAgIyBUcmFuc3Bvc2UgZGF0YSwgYnkgdXNpbmcgU1JFUi0yMDE3IEg1IGZpbGVzIHRoaXMgaXMgbmVlZGVkCiAgb3V0IDwtdChvdXQpCiAgCiAgIyBTZXQgbm8tZGF0YSB2YWx1ZSAKICBvdXRbb3V0ID09IG15Tm9EYXRhVmFsdWVdIDwtIE5BCiAgCiAgIyBUdXJuIG1hdHJpeCBpbnRvIGEgcmFzdGVyIHdpdGggY3JzCiAgb3V0ciA8LSByYXN0ZXIob3V0LGNycz1teV9jcnMpCiAgCiAgIyBTZXQgdmFyaWFibGVzIHdpdGggZXh0ZW50IGZvciB0aGUgcmFzdGVyIGJhc2VkIG9uIHRoZSBSZWZsZWN0YW5jZV9EYXRhIGF0dHJpYnV0ZXMKICB4TWluIDwtIHJlZmxJbmZvJFNwYXRpYWxfRXh0ZW50X21ldGVyc1sxXQogIHhNYXggPC0gcmVmbEluZm8kU3BhdGlhbF9FeHRlbnRfbWV0ZXJzWzJdCiAgeU1pbiA8LSByZWZsSW5mbyRTcGF0aWFsX0V4dGVudF9tZXRlcnNbM10KICB5TWF4IDwtIHJlZmxJbmZvJFNwYXRpYWxfRXh0ZW50X21ldGVyc1s0XQogIAogICMgQ3JlYXRlIGV4dGVudAogIHJhc0V4dCAgPC0gZXh0ZW50KHhNaW4seE1heCx5TWluLHlNYXgpCiAgCiAgIyBTZXQgZXh0ZW50IHRvIHJhc3RlcgogIGV4dGVudChvdXRyKSA8LSByYXNFeHQKICBINWNsb3NlKCkKICAjIFJldHVybiByYXN0ZXIgb2JqZWN0CiAgCiAgcmV0dXJuKG91dHIpCn0KCgojIFNldCB3cml0aW5nIGFuZCBmaWxlcyBsb2NhdGlvbgojd3JpdGVQYXRoIDwtICJ+L1JFTU9URV9TRU5TSU5HL05FT04vU1JFUi9DQU1QQUlHTl8yMDE3L1NQRUNUUk9NRVRFUi9SRUZMRUNUQU5DRS9ORU9OX1RNUC8iCiMgR2V0IGFsbCB0aGUgaDUgZmlsZSBuYW1lIHJlZmVyZW5jZXMgaW50byBhIGxpc3QKIyBHZXQgdGhlIGZ1bGwgcGF0aApmaWxlcyA8LSBsaXN0LmZpbGVzKHBhdGg9Ii9mdWxsUGF0aC9ORU9OX1dHRVdfMjAxOC9MMy9TcGVjdHJvbWV0ZXIvUmVmbGVjdGFuY2UiLCBwYXR0ZXJuPSIqLmg1IiwgZnVsbC5uYW1lcz1ULCByZWN1cnNpdmU9RkFMU0UpCiMgR2V0IHRoZSBpbmRleCBmcm9tIHRoZSBmaWxlbmFtZSBlLmc6IEZvciB0aGUgZmlsZSAvZnVsbFBhdGgvTkVPTl9EMTRfV0dFV19EUDNfNjA4MDAwXzM1MTUwMDAuaDUgCnAgPC0gZmlsZXNbMV0KIyBUaGlzIGluZGV4IGlzIG5lZWRlZCB0byBjcmVhdGUgdGhlIHRpZiBmaWxlcyAKZl9pbmRleCA8LSB1bmxpc3QoZ3JlZ2V4cHIoIk5FT05fRCIscCkpWzFdCgoKCiMgTG9vcCB0aHJvdWdoIGVhY2ggaDUgZmlsZSBhbmQgZXh0cmFjdCBhbmQgc2F2ZSBpdHMgYmFuZHMgYXMgR2VvVGlmIAojc2V0d2QoJ34vREFUQS9ORU9OX0RBVEEvUkVGTEVDVEFOQ0UvJykKZm9yIChmaSBpbiBmaWxlcykgewogIHZfYmFuZHMgPC0gbGlzdCgxOjQyNikKICBwcmludChwYXN0ZTAoIlByb2Nlc3NpbmcgZmlsZSAiLCBmaSkpCiAgcHRtIDwtIHByb2MudGltZSgpCiAgdl9yYXN0IDwtIGxhcHBseSgxOjQyNiwgQ29udmVydEJhbmQyUmFzdGVyLCBmID0gZmkpCiAgcHJvYy50aW1lKCkgLSBwdG0KICB2X3N0YWNrIDwtIHN0YWNrKHZfcmFzdCkKICB2X2JhbmRfbmFtZXMgPC0gcGFzdGUoIkJhbmRfIiwgdW5saXN0KHZfYmFuZHMpLHNlcD0iIikKICBuYW1lcyh2X3N0YWNrKSA8LSB2X2JhbmRfbmFtZXMKICAKICBmb3IgKGkgaW4gdW5saXN0KHZfYmFuZHMpKSB7CiAgICByX25hbWUgPC0gcGFzdGUwKHN1YnN0cihnc3ViKHBhdHRlcm49IlxcLmg1IiwiIixmaSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmX2luZGV4LG5jaGFyKGZpKSksCiAgICAgICAgICAgICAgICAgICAgICdfYmFuZF8nLCBzcHJpbnRmKCIlMDNkIixpKSwnLnRpZicpCiAgICBwcmludChwYXN0ZTAoIlByb2Nlc3NpbmcgZmlsZSAiLCByX25hbWUpKQogICAgd3JpdGVSYXN0ZXIodl9zdGFja1tbaV1dLCBmaWxlbmFtZT1yX25hbWUsIGZvcm1hdD0iR1RpZmYiKQogIH0KICByZW1vdmVUbXBGaWxlcyhoPTApCiAgZ2MoKQp9CgpgYGAKQXQgdGhpcyBwb2ludCB0aGVyZSBpcyBhIHNpbmdsZSBHZW9UaWYgcGVyIGJhbmQtZmlsZSBzdG9yZWQuCgpUaGUgbmV4dCBzdGVwcyBhcmUgcGVyZm9ybWVkIGluIGBiYXNoYCB0byBvcmdhbml6ZSB0aGUgZmlsZXMgZnJvbSBlYWNoIGJhbmQgYW5kIApwcmVwYXJlIHRoZSBtdWx0aWJhbmQgR2VvVGlmcyB0aGF0IHdpbGwgYmUgaW5nZXN0ZWQgaW50byBHRUUuIDxicj48YnI+CgojIyMjIDEwLjIgTWVyZ2UgYmFuZHMgYmFjayAodGlmKSBpbnRvIHNpbmdsZSBtdWx0aS1iYW5kIEdlb1RpZiAKYGBge2Jhc2ggY3JlYXRlTWVyZ2V9CiMgKioqKioqKioqKioqKioqKioqKiogQ29tbWFuZCBMaW5lIEluc3RydWN0aW9ucyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiojCiMgRG8gdGhpcyBieSBkYXRlLCB0aGUgZm9sbG93aW5nIGlzIGp1c3QgYW4gZXhhbXBsZSBvZiBhIHNpbmdsZSBsaW5lIGFuZCB0aGUgCiMgdGhlIGZpbGUgbmFtaW5nIGNoYW5nZXMgZGVwZW5kaW5nIG9uIHRoZSBzaXRlLCB0aGlzIGNhc2UgaXMgZnJvbSBTUkVSIHNpdGUuCiMgZS5nLiAKbHMgTkVPTl9EMTRfU1JFUl9EUDFfMjAxNzA4MjRfMTYwNTQ2X3JlZmxlY3RhbmNlX2JhbmRfKi50aWYgPiBidGlmLnR4dCAgCmVjaG8gY2F0IGJ0aWYudHh0ID4gcHJvY2Vzc2g1LnNoCnZpIHByb2Nlc3NoNS5zaAoKIyBUaGlzIGlzIHRoZSBvbmUtbGluZSBpbnN0cnVjdGlvbiBpbiBSZWRoYXQgY29tbWFuZCBsaW5lLCBzaG91bGQgd29yayBpbiBhbnkgCiMgbGludXggZmxhdm9yLiBDaGFuZ2UgIk5FT05fRDE0X1NSRVJfRFAzIiBzZWN0aW9uIGZvciB0aGUgY29ycmVzcG9uZGluZyBzaXRlCmZpbmQgLXR5cGUgZiAtbmFtZSAnKi50aWYnIHwgYXdrICdCRUdJTntGUz0iXyJ9eyBwcmludCAiTkVPTl9EMTRfU1JFUl9EUDNfIiQ1Il8iJDYiX3JlZmxlY3RhbmNlX2JhbmRfKi50aWYifScgfCBzb3J0IHwgdW5pcXVlID4gYnRpZi50eHQKZWNobyBjYXQgYnRpZi50eHQgPiBwcm9jZXNzaDUuc2gKCiMgVGhlIHByZXZpb3VzIG9uLWxpbmVyIGlzIHdoYXQgeW91IG5lZWQgdG8gcnVuIGZvciBhbGwgdGhlIGZpbGVzCgojIFRvIG1ha2UgdGhlIHByZXZpb3VzIGNvbW1hbmQgZm9yIHNldmVyYWwgZmlsZXMgb3BlbiB0aGUgZmlsZSBgcHJvY2Vzc2g1LnNoIAojIGluIGB2aWAgYW5kIHJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOgoKIyBJbnNlcnQgYGVjaG9gIHN0YXJ0aW5nIGF0IGVhY2ggcm93IAo6JXMgL14vZWNobyAvZyAKIyBJbnNlcnQgYCA+PiBhbGx0aWZzLnR4dGAgYXQgdGhlIGVuZCBvZiByb3dzCiVzIC8kLyA+PiBhbGx0aWZzLnR4dC9nIAoKIyBNb3ZlIHRvIHRoZSBmaXJzdCByb3cgb2YgdGhlIGZpbGUocHJvY2Vzc2g1LnNoKSBhbmQgY2hhbmdlIGA+PmAgYnkgdGhpcyBgPmAgdG8gY3JlYXRlIGluc3RlYWQgb2YgYD4+YCB3aGljaCBpcyBmb3IgYXBwZW5kaW5nLgoKIyBZb3Ugc2hvdWxkIG5vdyBzZWUgc29tZXRoaW5nIGxpa2UgdGhpcyBpbiB0aGUgZmlsZTogIAplY2hvIE5FT05fRDE0X1NSRVJfRFAxXzIwMTcwODI0XzE2MTY1NV9yZWZsZWN0YW5jZV9iYW5kXyoudGlmID4gYWxsdGlmcy50eHQKZWNobyBORU9OX0QxNF9TUkVSX0RQMV8yMDE3MDgyNF8xNjI0MDdfcmVmbGVjdGFuY2VfYmFuZF8qLnRpZiA+PiBhbGx0aWZzLnR4dAplY2hvIE5FT05fRDE0X1NSRVJfRFAxXzIwMTcwODI0XzE2MzEwNl9yZWZsZWN0YW5jZV9iYW5kXyoudGlmID4+IGFsbHRpZnMudHh0CmVjaG8gTkVPTl9EMTRfU1JFUl9EUDFfMjAxNzA4MjRfMTY0MDQ5X3JlZmxlY3RhbmNlX2JhbmRfKi50aWYgPj4gYWxsdGlmcy50eHQKZWNobyBORU9OX0QxNF9TUkVSX0RQMV8yMDE3MDgyNF8xNjQ0NTNfcmVmbGVjdGFuY2VfYmFuZF8qLnRpZiA+PiBhbGx0aWZzLnR4dAouLi4KCiMgYW5kIHNvIG9uLCBmb3IgZWFjaCBGSUxFLURBVEXigKYKIyBDbG9zZSBhbmQgUnVuIHRoZSBmaWxlIGBwcm9jZXNzaDUuc2hgCmNobW9kICt4IHByb2Nlc3NoNS5zaAouL3Byb2Nlc3NoNS5zaAoKIyBBZnRlciBydW5uaW5nIGBwcm9jZXNzaDUuc2hgIHlvdSBzaG91bGQgZ2V0IGBhbGx0aWZzLnR4dGAgZmlsZSwgdGhlbiBqb2luIGFsbCAKIyB0aGUgZmlsZW5hbWVzIGludG8gYSBzaW5nbGUgbGluZS4KCiMgVXNpbmcgYGF3a2AgOy0pIApjYXQgYWxsdGlmcy50eHQgfCBhd2sgJ3twcmludH0nIE9SUz0nICcgPiBhbGxfYmFuZHNfZmlsZXMKCiMgVGhlIGBhbGxfYmFuZHNfZmlsZXNgIGlzIHVzZWQgd2l0aCBnZGFsIHRvb2xzIHRvIGNyZWF0ZSBtb3NhaWNzCgpgYGAKCkluIHRoZSBmb2xsb3dpbmcgYmxvY2ssIEdEQUwgVG9vbHMgKGdkYWxidWlsZHZydCkgYXJlIHVzZWQgdG8gY3JlYXRlIGEgdmlydHVhbCBmaWxlCmFuZCB0aGVuIGdlbmVyYXRlIHRoZSBmaW5hbCBtdWx0aWJhbmQgZmlsZS4KCmBgYHtiYXNoIG1lcmdlQmFuZHN9CiMgVGhlIGdvYWwgb2YgdGhpcyBzY3JpcHQgaXMgdG8gd29yayB3aXRoIEdEQUwgdG9vbHMgYGdkYWxidWlsZHZydGAgYW5kIAojIGBnZGFsX3RyYW5zbGF0ZWAuIFdpdGggdGhlc2UgdG9vbHMgaXMgcG9zc2libGUgdG8gY3JlYXRlIGEgbXVsdGktYmFuZCBmaWxlLiAgCiMgCiMgKioqKioqKioqKioqKioqKioqKiogQ29tbWFuZCBMaW5lIEluc3RydWN0aW9ucyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiojCiMgSW5zZXJ0IHRoZSBmb2xsb3dpbmcgbGluZSBpbiB0aGUgYmFzaCBmaWxlLiBXaGVyZSBgYWxsX2JhbmRzX2ZpbGVzYCBpcyBhIG9uZSBsaW5lCiMgdGV4dCBmaWxlIHdpdGggYWxsIHRoZSBmaWxlIG5hbWVzIG9mIHRoZSB0aWYgZmlsZXMgZm9yIGVhY2ggYmFuZCBpbiBhIHNpbmdsZSBsaW5lIAojIGZvciBlYWNoIGRhdGUgaXQgaXMgcmVxdWlyZWQgdG8gZ2V0IG9uZSBjYWxsIHRvIGBnZGFsYnVpbGR2cnQuCgojIEFnYWluLCB0aGlzIGlzIGp1c3QgYSBzaW5nbGUgZXhhbXBsZSBhbmQgeW91IGRvbid0IGhhdmUgdG8gcnVuIHRoaXMuLi4KZ2RhbGJ1aWxkdnJ0IC1zZXBhcmF0ZSBORU9OX1dHRVdfMjAxOC52cnQgTkVPTl9GSUxFX05BTUVfREFURS50aWYgIGFsbF9iYW5kc19maWxlcwoKIyBGb3IgU1JFUiAyMDE4IHRoZXNlIG9uZS1saW5lcnMgd2VyZSB1c2VkCiMgVGhpcyBpcyB1c2VkIHRvIGJ1aWxkIHRoZSBjYWxscyB0byBgZ2RhbGJ1aWxkdnJ0YAojIFRoaXMgb25lLWxpbmVyIGNhbiByZWFkIGFzOiBMaXN0IGFsbCB0aGUgJ3RpZicgZmlsZXMsIHRoZW4gc3VidHJhY3QgYSBwb3J0aW9uIAojIG9mIHRoZSBmaWxlbmFtZSB0byBjcmVhdGUgdGhlIC52cnQgZmlsZSwgdGhlbiBwcmUtYXBwZW5kIHRvIGVhY2ggLnZydCBmaWxlIHRoZSAKIyBpbnN0cnVjdGlvbiBgZ2RhbGJ1aWxkdnJ0IC1zZXBhcmF0ZWAgYW5kIHNlbmQgdGhlIHJlc3VsdHMgaW50byAKIyBgZ2RhbGJ1aWxkX2ZpcnN0X3BhcnQudHh0YCAgRG9uJ3QgZm9yZ2V0IHRvIGNoYW5nZSB0aGUgZmlsZSBuYW1lLi4uIApmaW5kIC10eXBlIGYgLW5hbWUgJyoudGlmJyB8IGF3ayAnQkVHSU57RlM9Il8ifXsgcHJpbnQgIk5FT05fRDE0X1NSRVJfRFAzXyIkNSJfIiQ2Il9yZWZsZWN0YW5jZV9iYW5kXyoudGlmIn0nIHwgc29ydCB8IHVuaXEgfCBhd2sgJ3twcmludCAiZ2RhbGJ1aWxkdnJ0IC1zZXBhcmF0ZSAic3Vic3RyKCQwLDAsMzIpIi52cnQgIn0nID4gZ2RhbGJ1aWxkX2ZpcnN0X3BhcnQudHh0CgojIFRoZSBvdXRwdXQgc2hvdWxkIGhhdmUgYXMgbWFueSBsaW5lcyBhcyB0aGUgbnVtYmVyIG9mIGAuaDVgIGZpbGVzLiAgCiMgU29tZXRoaW5nIGxpa2U6IGdkYWxidWlsZHZydCAtc2VwYXJhdGUgTkVPTl9EMTRfU1JFUl9EUDNfNjA4MDAwXzM1MTUwMDAudnJ0CgojIE5vdywgY3JlYXRlIGFuIGluZGV4IHRvIG1ha2UgYSBqb2luIGxhdGVyCmF3ayAne3ByaW50ZiAoIiUuM2QgJXNcbiIsIE5SLCAkMCkgfScgYWxsdGlmcy50eHQgPiBhbGx0aWZzMi50eHQKCiMgQ3JlYXRlIGFub3RoZXIgaW5kZXgKYXdrICd7cHJpbnRmICgiJS4zZCAlc1xuIiwgTlIsICQwKSB9JyBnZGFsYnVpbGRfZmlyc3RfcGFydC50eHQgID4gZ2RhbF90ZW1wLnR4dAoKIyBDcmVhdGUgb25lLWxpbmVyIGZvciBnZGFsYnVpbGR2cnQsIHRoaXMgaXMgZG9uZSB0byBhY2hpZXZlIHRoZSBmdWxsIG9uZS1saW5lIAojIGdkYWxidWlsZHZydCBpbnN0cnVjdGlvbi4Kam9pbiAtMSAxIC0yIDEgZ2RhbF90ZW1wLnR4dCBhbGx0aWZzMi50eHQgPiBvbmUtbGluZXIuc2gKCiMgUmVtb3ZlIGluZGV4IGNvbHVtbiBmcm9tIHRoZSByZXN1bHRpbmcgam9pbgojIFNpbmNlIHRoZSBqb2luZWQgY29sdW1uIHN0YXJ0IGxpa2UgdGhpczogMDAxIE5FT04uLi4KIyBUaGUgZmlyc3QgNCBjaGFyYWN0ZXJzIHNob3VsZCBiZSByZW1vdmVkLCB0aGVyZWZvcmUgdXNlIHRoaXMKc2VkICdzL14uXHs0XH0vLycgb25lLWxpbmVyLnNoICA+IG9uZS1saW5lcjIuc2gKCiMgb25lLWxpbmVyLnNoIHdpbGwgY29udGFpbiB0aGUgZnVsbCBpbnN0cnVjdGlvbiB0byBjcmVhdGUgYWxsIHRoZSB2cnQgZmlsZXMKIyBSdW4gb25lLWxpbmVyMi5zaCB0byBnZXQgYHZydGAgZmlsZXMKY2htb2QgK3ggb25lLWxpbmVyMi5zaAouL29uZS1saW5lcjIuc2gKCiMgVGhlIG5leHQgY29tbWFuZCBpcyB0byB1c2UgdGhlIHZpcnR1YWwgZmlsZXMgYW5kIGdlbmVyYXRlIHRoZSBtdWx0aS1iYW5kIGZpbGUKIyB0aGUgb3B0aW9ucyB3ZXJlIHByb3ZpZGVkIGJ5IHRoZSBHRUUgZGF0YSBpbmdlc3Rpb24gdGVhbS4KCiMgUmVwZWF0IHRoZSBmb2xsb3dpbmcgaW5zdHJ1Y3Rpb24gYXMgbWFueSB0aW1lcyBhcyBkaWZmZXJlbnQgbXVsdGktYmFuZHMgd2lsbCAKIyBiZSBjcmVhdGVkLiBTZWUgdGhlIGNvbW1hbmQgbGluZSB1c2VkIHRvIGdlbmVyYXRlIGEgZmlsZSB3aXRoIHRoaXMgaW5zdHJ1Y3Rpb24KIyBvbiBlYWNoIGB2cnRgIGZpbGUuCgpnZGFsX3RyYW5zbGF0ZSAtLWNvbmZpZyBHREFMX0NBQ0hFTUFYIDEwMjQgLW9mIEdUaWZmIC1jbyBDT01QUkVTUz1ERUZMQVRFIC1jbyBaTEVWRUw9OSAtY28gQklHVElGRj1JRl9TQUZFUiAtY28gSU5URVJMRUFWRT1CQU5EIC1jbyBOVU1fVEhSRUFEUz02MCBORU9OX0ZJTEVfTkFNRV9EQVRFLnZydCBORU9OX0QxNF9TUkVSX0QuLi5tYi50aWYKCiMgQSBvbmUtbGluZXIgdG8gY3JlYXRlIGEgImJhc2giIGZvciBydW5uaW5nIG92ZXIgYWxsIHRoZSBgdnJ0YCBmaWxlcyAKIyBjb3VsZCBiZSBzb21ldGhpbmcgbGlrZSB0aGlzOgpmaW5kIC4vIC1tYXhkZXB0aCAxIC1uYW1lICIqLnZydCIgIHwgc2VkICdzL1wudnJ0JC8vJyB8IHNlZCAncy9cLlwvLy8nIHwgYXdrICd7cHJpbnQgImdkYWxfdHJhbnNsYXRlIC0tY29uZmlnIEdEQUxfQ0FDSEVNQVggMTAyNCAtb2YgR1RpZmYgLWNvIENPTVBSRVNTPURFRkxBVEUgLWNvIFpMRVZFTD05IC1jbyBCSUdUSUZGPUlGX1NBRkVSIC1jbyBJTlRFUkxFQVZFPUJBTkQgLWNvIE5VTV9USFJFQURTPTYwICIkMCIudnJ0ICIkMCJfcmVmbGVjdGFuY2VfbWIudGlmIn0nID4gY3JlYXRlX211bHRpX2JhbmRfZmlsZXMuc2gKCiMgUnVuIHRoZSByZXN1bHRpbmcgZmlsZSBhcyBiYXNoIHNjcmlwdDoKY2htb2QgK3ggY3JlYXRlX211bHRpX2JhbmRfZmlsZXMuc2gKLi9jcmVhdGVfbXVsdGlfYmFuZF9maWxlcy5zaAoKIyBBdCB0aGlzIHBvaW50LCB5b3Ugc2hvdWxkIGhhdmUgYSBsaXN0IG9mIGB0aWZgIGZpbGVzIHdoZXJlIGVhY2ggZmlsZSBzaG91bGQgCiMgaGF2ZSB0aGUgNDI2IGJhbmRzLiAgSW4gdG90YWwsIGZvciBXR0VXIDIwMTggc3VydmV5LCAzNjkgZmlsZXMgKC5oNSkgd2VyZSBwcm9kdWNlZC4KCgpgYGAKVGhlIGBnZGFsX3RyYW5zbGF0ZWAgaXMgdGhlIGxhc3Qgc3RlcCBpbiBjcmVhdGluZyBtdWx0aWJhbmQgZmlsZXMgYXMgR2VvVGlmLiBBZnRlcgp0aGlzIHN0ZXAgdGhlIG91dHB1dCBmaWxlcyBhcmUgcmVhZHkgdG8gYmUgc2VudCB0byBHb29nbGUgQ2xvdWQgU3RvcmFnZSBidWNrZXQgYW5kCnRoZW4gdHJhbnNmZXJyZWQgaW50byBHRUUuIDxicj48YnI+CgojIyMjIDEwLjMgVXBsb2FkIGVhY2ggbXVsdGktYmFuZCBnZW90aWYgaW50byBHRUUgKExvY2FsIC0tPiBHQ1MgLS0+IEdFRSkKClN0YXJ0IGJ5IG1vdmluZyBtdWx0aWJhbmQgZmlsZXMgdG8gR0NTLiBVc2luZyB0aGUgYGdzdXRpbGAgdG9vbCBjb3B5IAp0aGUgbXVsdGktYmFuZCBnZW90aWYgZmlsZXMgaW50byB0aGUgR0NTLWJ1Y2tldC4gCgoKYGBge2Jhc2ggdXBsb2FkMkdDU30KIyBUaGUgYnVja2V0L2ZvbGRlciBzdHJ1Y3R1cmUgYmVsb3cgd2FzIGNyZWF0ZWQgaW4gdGhlIEdvb2dsZSBDbG91ZCBDb25zb2xlIHdlYnNpdGUKCmdzdXRpbCBjcCAqbWIudGlmIGdzOi8vZ2VlX25lb25fc3Jlcl8yMDE4L0wzL1NwZWN0cm9tZXRlci9SZWZsZWN0YW5jZXMvCgpgYGAKT25jZSBmaWxlcyBhcmUgaW4gdGhlIEdDUyBidWNrZXQgdGhlIGZvbGxvd2luZyBzdGVwIGlzIHRvIG1vdmUgdGhvc2UgZmlsZXMgaW50bwpHRUUgYXMgYW4gaW1hZ2UgY29sbGVjdGlvbi4gRm9yIGRvaW5nIHRoaXMsIHNhdmUgdGhlIGZvbGxvd2luZyBiYXNoIHNjcmlwdCBpbiBhIApmaWxlIGFuZCBydW4gaXQgaW4gdW5peCB3aGVyZSB5b3UgYXJlIGF1dGhlbnRpY2F0ZWQgdG8gZ29vZ2xlIGNsb3VkIGFuZCAKZWFydGhlbmdpbmUuCgpgYGB7YmFzaCBtb3ZlMmdlZX0KCiMgKioqKioqKioqKioqKioqKioqKiogQmFzaCBzY3JpcHQgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiojCiMhL2Jpbi9iYXNoCmltZ0ZvbGRlcj0idXNlcnMvZ3BvbmNlL3VzZGFfYXJzL2ltYWdlX2NvbGxlY3Rpb25zL25lb24vc3Jlcl8yMDE4L25lb25fc3Jlcl8yMDE4X2hzIgojIEZpcnN0IHRpbWUsIGNyZWF0ZSBjb2xsZWN0aW9uCmVhcnRoZW5naW5lIGNyZWF0ZSBjb2xsZWN0aW9uICRpbWdGb2xkZXIKIyBCdWNrZXQgaW4gR0NTCmltZ0ZvbGRlcj0kaW1nRm9sZGVyIi8iCnN0clByb3Y9IihzdHJpbmcpcHJvdmlkZXI9QkFUVEVMTEVfTkVPTl9TUkVSXzIwMTgiCmdjQnVja2V0PSJnZWVfbmVvbl9zcmVyXzIwMTgvTDMvU3BlY3Ryb21ldGVyL1JlZmxlY3RhbmNlcy8iCnZkYXRlPSIyMDE4LTA4LTMwVDEyOjAwOjAwIgojIENyZWF0ZSBhc3NldCBpbiBHRUUKZm9yIGZpbGUgaW4gKl9tYi50aWY7IGRvCiAgICAjIFJlbW92ZSAudGlmIAogICAgbmFtZT0kKGVjaG8gJGZpbGUgfCBjdXQgLWYgMSAtZCAnLicpCiAgICBhc3NldD0kaW1nRm9sZGVyJG5hbWUKICAgICMgRm9yIHRlc3RpbmcgYmVmb3JlIHJ1bm5pbmcsIHVzZSB0aGUgZm9sbG93aW5nIGVjaG8gc3RhdGFtZW50IHRvIHByaW50IG91dCAKICAgICMgdGhlIGZpbmFsIGNvbW1hbmQuCiAgICAKICAgICNlY2hvIGVhcnRoZW5naW5lIHVwbG9hZCBpbWFnZSAtLWFzc2V0X2lkPSIke2Fzc2V0fSIgLS10aW1lX3N0YXJ0PSIke3ZkYXRlfSIgLS1wcm9wZXJ0eT0iJHtzdHJQcm92fSIgLS1weXJhbWlkaW5nX3BvbGljeT1zYW1wbGUgZ3M6Ly8kZ2NCdWNrZXQkZmlsZQogICAgCiAgICAjIENhbGwgdGhlIGluZ2VzdGlvbiBjb21tYW5kIHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgcGFyYW1ldGVycyB0byBwb3B1bGF0ZSBwcm9wZXJ0aWVzIGF0IGVhY2ggaW1hZ2UgaW5nZXN0ZWQKICAgIGVhcnRoZW5naW5lIHVwbG9hZCBpbWFnZSAtLWFzc2V0X2lkPSIke2Fzc2V0fSIgLS10aW1lX3N0YXJ0PSIke3ZkYXRlfSIgLS1wcm9wZXJ0eT0iJHtzdHJQcm92fSIgLS1weXJhbWlkaW5nX3BvbGljeT1zYW1wbGUgZ3M6Ly8kZ2NCdWNrZXQkZmlsZQpkb25lCgpgYGAKVGhpcyBjb25jbHVkZXMgdGhlIHByb2Nlc3MgZm9yIHVwbG9hZGluZyBzcGVjdHJvbWV0ZXIgcmVmbGVjdGFuY2VzIGZyb20gTkVPTiAKaW50byBHb29nbGUgRWFydGggRW5naW5lLiAKPGJyPjxicj4K